package com.hissy.common.generator_01;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import jdk.internal.org.objectweb.asm.Opcodes;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

/**
 * Created by zhangyanfei on 2018/5/29.
 * ASM 字节码操作
 *
 * @author zhangyanfei
 * @date 2018/05/29
 */
public class POJOSerializerProvider implements Opcodes {
    private final static ConcurrentHashMap<Class<?>, Serializer<?>> cache
        = new ConcurrentHashMap<Class<?>, Serializer<?>>();

    /**
     * 返回实体类的序列化类对象
     * 创建以后不用重新创建
     */
    public static <T> Serializer<T> getSerializer(Class<T> clazz) {
        Serializer<T> s = (Serializer<T>)cache.get(clazz);
        if (s == null) {
            synchronized (cache) {
                s = (Serializer<T>)cache.get(clazz);
                if (s == null) {
                    s = build(clazz);
                    cache.put(clazz, s);
                }
            }
        }
        return s;
    }

    /**
     * 构建类的序列化器
     *
     * @param clazz
     * @param <T>
     * @return
     */
    private static <T> Serializer<T> build(Class<T> clazz) {
        HashMap<String, Integer> map = new HashMap<>();
        LinkedList<String> list = new LinkedList<>();
        String className = "com.yit.autogen.Serializer_" + clazz.getName().replace('.', '_').replace('$', '_');
        String c_name = className;
        String c_desc = "L" + c_name + ";";
        //JVM内部描述
        String t_className = clazz.getName().replace('.', '/');
        String t_classDesc = Type.getDescriptor(clazz);

        try {
            Field[] dfs = clazz.getDeclaredFields();
            List<Field> fds = new ArrayList<>();
            //过滤掉私有或者静态成员变量
            fds = fds.stream()
                .filter(f -> {
                    int modifier = f.getModifiers();
                    return Modifier.isPrivate(modifier);
                }).collect(Collectors.toList());
            //类 指定系统计算栈桢大小
            ClassWriter cw = new PocClassWriter(ClassWriter.COMPUTE_FRAMES);
            //操作方法的visit
            MethodVisitor mv;
            /**
             * Opcodes.V1_5指定类的版本
             Opcodes.ACC_PUBLIC表示这个类是public
             “com/sunchao/asm/HelloWorld”类的全限定名称
             第一个null位置变量定义的是泛型签名，
             “java/lang/Object”这个类的父类
             第二个null位子的变量定义的是这个类实现的接口
             */
            cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, c_name,
                "Ljava/lang/Object;Lcom/hissy/common/generator_01/Serializer<" + t_classDesc + ">;", "java/lang/Object",
                new String[] {Serializer.class.getName().replace('.', '/')});
            //存在內部类
            if (clazz.getDeclaringClass() != null) {
                BytecodeUtil.createInnerClassVisitor(cw, clazz);
            }
            {
                //成员变量
                FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "bs", "[[B", null, null);
                fv.visitEnd();
            }
            {
                //构造方法
                mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
                //开启 访问方法指令
                mv.visitCode();
                // A position in the bytecode of a method.
                Label l0 = new Label();
                mv.visitLabel(l0);
                //将label放到 局部变量栈的0号位置
                mv.visitVarInsn(ALOAD, 0);
                //调用父类方法
                mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
                mv.visitInsn(RETURN);
                Label l1 = new Label();
                mv.visitLabel(l1);
                //本地变量
                mv.visitLocalVariable("this", c_desc, null, l0, l1, 0);
                mv.visitMaxs(1, 1);
                mv.visitEnd();
            }

        } catch (Exception e) {

        }
        return null;
    }
}
